<!--
 * @description: WebSocket 封装组件，用于连接 WebSocket 服务端，接收数据并处理
 * @author: XXX
 * @Date: 2023-08-17 09:54:34
 * @Modified By: --
 * @version: 1.0.0
-->
<template>
  <div style="display: none"></div>
</template>

<script>
export default {
  name: 'Websocket',
  props: {
    title: String, // 标题
    host: {
      type: String,
      default: window.location.host
    },
    // url连接地址
    url: {
      type: String,
      default: ''
    },
    // 传给后台的参数
    sendData: {
      type: Object,
      default: () => ({})
    },
    urlShow: {
      type: Boolean,
      default: true
    },
    // WebSocket 数据，对数据进行处理的回调函数
    successCallback: Function,
    // WebSocket 连接错误的回调函数
    errorCallback: Function
  },
  data() {
    return {
      wsObj: null, // WebSocket 对象
      lockReconnect: false, // 避免重复连接
      wsCreateHandler: null, // 重连定时器
      socketLeaveFlag: false, // 离开标记（解决 退出登录再登录 时出现的 多次相同推送 问题，出现的本质是多次建立了WebSocket连接）
      // 心跳检测
      heartCheck: {
        vueThis: this, // vue实例
        timeout: 15000, // 超时时间
        timeoutObj: null, // 计时器对象——向后端发送心跳检测
        serverTimeoutObj: null, // 计时器对象——等待后端心跳检测的回复
        reset() {
          this.timeoutObj && clearTimeout(this.timeoutObj)
          this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj)
          return this
        },
        start() {
          // 开启定时器发送 ping 到后台
          this.timeoutObj = setTimeout(() => {
            this.sendPingToBackend()

            // 15s之内如果没有收到后台的消息，则认为连接断开了，需要重连
            this.serverTimeoutObj = setTimeout(() => {
              console.log('未收到心跳检测回复')
              this.vueThis.reconnect()
            }, this.timeout)
          }, this.timeout)
        },
        sendPingToBackend() {
          if (this.wsObj && this.wsObj.readyState === this.wsObj.OPEN) {
            const data = { ping: true }
            this.wsObj.send(JSON.stringify(data))
            console.log('发送心跳检测')
          }
        }
      }
    }
  },
  methods: {
    // 关闭 WebSocket
    closeWebSocket() {
      if (this.wsObj) {
        this.wsObj.close()
        this.lockReconnect = true
        this.wsCreateHandler && clearTimeout(this.wsCreateHandler)
        this.heartCheck.reset()
      }
    },
    // 创建 WebSocket
    createWebSocket() {
      if (typeof WebSocket === 'undefined') {
        console.warn('您的浏览器不支持WebSocket，无法获取数据')
        return false
      }
      const SplicingUrl = `ws://192.168.1.214:16223${this.url}/${this.sendData.agentId}/`
      let webSocketLink = this.urlShow ? SplicingUrl : this.url
      try {
        this.wsObj = new WebSocket(webSocketLink)
        this.initWsEventHandle()
      } catch (e) {
        this.writeToScreen('连接错误，尝试重新连接！')
        this.reconnect()
      }
    },
    // 初始化 WebSocket 事件处理
    initWsEventHandle() {
      try {
        // 连接成功
        this.wsObj.onopen = event => {
          this.onWsOpen(event)
          this.heartCheck.reset().start()
        }

        // 监听服务器端返回的信息
        this.wsObj.onmessage = event => {
          this.onWsMessage(event)
          this.heartCheck.reset().start()
        }

        // 连接关闭
        this.wsObj.onclose = event => {
          this.onWsClose(event)
        }

        // 连接错误
        this.wsObj.onerror = event => {
          this.onWsError(event)
          this.reconnect()
        }
      } catch (err) {
        this.writeToScreen('事件绑定失败，尝试重新连接')
        this.reconnect()
      }
      // 监听窗口关闭事件，当窗口关闭时，主动去关闭websocket连接，防止连接还没断开就关闭窗口，server端会抛异常。
      window.onbeforeunload = () => {
        this.wsObj.close()
      }
    },
    // WebSocket 连接成功处理
    onWsOpen(event) {
      console.log(`${this.title}:WebSocket-连接成功:", ${event}`)
      if (this.wsObj.readyState === this.wsObj.OPEN) {
        this.wsObj.send(JSON.stringify(this.sendData.data))
      }
    },
    // WebSocket 收到消息处理
    onWsMessage(event) {
      console.log(event, this.title)
      if (event.data.indexOf('HeartBeat') > -1) {
        // 心跳回复——心跳检测重置
        // 收到心跳检测回复就说明连接正常
        console.log('收到心跳检测回复')
        // 心跳检测重置
        return
      }
      if (event.data === '连接成功') {return}
      // 普通推送——正常处理
      // 相关处理
      const jsonStr = event.data
      this.successCallback(jsonStr)
    },
    // WebSocket 关闭连接处理
    onWsClose() {
      this.writeToScreen('WebSocket:已关闭')
      // 心跳检测重置
      this.heartCheck.reset()
      if (!this.socketLeaveFlag) {
        // 没有离开--重连
        // websocket重连
        this.reconnect()
      }
    },
    // WebSocket 错误处理
    onWsError(event) {
      console.warn(' WebSocket 错误:', event)
      this.errorCallback()
    },
    // 将消息写入控制台
    writeToScreen(message) {
      console.warn(`${this.title}:${message}`)
    },
    // 重连函数
    reconnect() {
      if (this.lockReconnect) {
        return
      }
      this.writeToScreen('3 秒后重连')
      this.lockReconnect = true
      this.wsCreateHandler && clearTimeout(this.wsCreateHandler)
      this.wsCreateHandler = setTimeout(() => {
        this.writeToScreen('重新连接中...')
        this.lockReconnect = false
        this.createWebSocket()
        this.writeToScreen('重新连接完成')
      }, 3000)
    }
  },

  // 组件销毁前关闭 WebSocket
  beforeDestroy() {
    // 离开标记
    this.socketLeaveFlag = true
    this.closeWebSocket()
  }
}
</script>
